package cn.dw.dao.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import cn.dw.dao.GenericDAO;
import cn.dw.utils.ConditionType;
import cn.dw.utils.PagerModel;
@SuppressWarnings({"unchecked","rawtypes","deprecation"})
public class GenericDAOImpl<T>
  extends HibernateDaoSupport
  implements GenericDAO<T>
{
  private static final long serialVersionUID = -2638934836888612350L;
  protected Class<T> entityClass;
  
  
public GenericDAOImpl()
  {
    this.entityClass = 
      ((Class)((java.lang.reflect.ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
  }
  
  protected Class<T> getEntityClass()
  {
    return this.entityClass;
  }
  
public Criteria createCriteria()
  {
    return getSession().createCriteria(getEntityClass());
  }
  
  public Criteria createCriteria(Criterion... criterions)
  {
    Criteria criteria = getSession().createCriteria(getEntityClass());
    for (Criterion c : criterions) {
      criteria.add(c);
    }
    return criteria;
  }
  
  protected Criterion getCondition(ConditionType ct)
  {
    if (ct != null)
    {
      int oper = ct.getIoperator();
      if (oper == 0) {
        return Restrictions.eq(ct.getName(), ct.getValue());
      }
      if (8 == oper) {
        return Restrictions.ne(ct.getName(), ct.getValue());
      }
      if (2 == oper) {
        return Restrictions.ge(ct.getName(), ct.getValue());
      }
      if (6 == oper) {
        return Restrictions.gt(ct.getName(), ct.getValue());
      }
      if (1 == oper) {
        return Restrictions.le(ct.getName(), ct.getValue());
      }
      if (7 == oper) {
        return Restrictions.lt(ct.getName(), ct.getValue());
      }
      if (4 == oper) {
        return Restrictions.isNull(ct.getName());
      }
      if (5 == oper) {
        return Restrictions.isNotNull(ct.getName());
      }
      if (3 == oper) {
        return Restrictions.like(ct.getName(), 
          ct.getValue().toString(), MatchMode.ANYWHERE);
      }
      if (9 == oper) {
        return Restrictions.in(ct.getName(), ct.getValues());
      }
      if (10 == oper) {
        return Restrictions.eqProperty(ct.getName(), ct.getOtherName());
      }
      if (11 == oper) {
        return Restrictions.leProperty(ct.getName(), ct.getOtherName());
      }
      if (12 == oper) {
        return Restrictions.geProperty(ct.getName(), ct.getOtherName());
      }
      if (13 == oper) {
        return Restrictions.gtProperty(ct.getName(), ct.getOtherName());
      }
      if (14 == oper) {
        return Restrictions.ltProperty(ct.getName(), ct.getOtherName());
      }
      if (15 == oper) {
        return Restrictions.neProperty(ct.getName(), ct.getOtherName());
      }
    }
    return null;
  }
  
  public Criteria createCriteria(List<ConditionType> conditionTypes)
  {
    if (conditionTypes == null) {
      return createCriteria();
    }
    Criteria criteria = getSession().createCriteria(getEntityClass());
    for (int i = 0; i < conditionTypes.size(); i++)
    {
      ConditionType ct = (ConditionType)conditionTypes.get(i);
      if (ct.getLinkType() != 12)
      {
        criteria.add(getCondition(ct));
      }
      else
      {
        Criterion tCriterion = null;
        Disjunction dj = Restrictions.disjunction();
        List<ConditionType> cts = ct.getConditionTypes();
        for (int j = 0; j < cts.size(); j++) {
          tCriterion = dj.add(getCondition((ConditionType)cts.get(j)));
        }
        if (tCriterion != null) {
          criteria.add(tCriterion);
        }
      }
    }
    return criteria;
  }
  
  protected int countByCriteria(Criteria criteria)
  {
    if (criteria == null) {
      return count();
    }
    return 
      ((Number)criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
  }
  
  protected List<T> findByCriteria(Criteria criteria)
  {
    return criteria.list();
  }
  
  public int count()
  {
    Criteria criteria = createCriteria();
    return countByCriteria(criteria);
  }
  
  public int count(ConditionType conditionType)
  {
    List<ConditionType> conditionTypes = new ArrayList();
    conditionTypes.add(conditionType);
    Criteria criteria = createCriteria(conditionTypes);
    return countByCriteria(criteria);
  }
  
  public int count(List<ConditionType> conditionTypes)
  {
    Criteria criteria = createCriteria(conditionTypes);
    return countByCriteria(criteria);
  }
  
  public T find(Serializable id)
  {
    return getHibernateTemplate().get(getEntityClass(), id);
  }
  
  public List<T> find(ConditionType conditionType)
  {
    if (conditionType == null) {
      return findAll();
    }
    List<ConditionType> conditionTypes = new ArrayList();
    conditionTypes.add(conditionType);
    Criteria criteria = createCriteria(conditionTypes);
    return criteria.list();
  }
  
  public List<T> find(List<ConditionType> conditionTypes)
  {
    if (conditionTypes == null) {
      return findAll();
    }
    Criteria criteria = createCriteria(conditionTypes);
    return criteria.list();
  }
  
  public List<T> find(List<ConditionType> conditionTypes, List<Order> orders)
  {
    if (orders == null) {
      return find(conditionTypes);
    }
    Criteria criteria = createCriteria(conditionTypes);
    for (int i = 0; i < orders.size(); i++) {
      criteria.addOrder((Order)orders.get(i));
    }
    return criteria.list();
  }
  
  public T findUniqueByProperty(String property, Object value)
  {
    Assert.hasText(property);
    Assert.notNull(value);
    return (T) createCriteria(new Criterion[] { Restrictions.eq(property, value) })
      .uniqueResult();
  }
  
  public T findUniqueByProperty(String[] property, Object[] value)
  {
    Criteria criteria = getSession().createCriteria(getEntityClass());
    for (int i = 0; i < property.length; i++) {
      criteria.add(Restrictions.eq(property[i], value[i]));
    }
    return (T) criteria.uniqueResult();
  }
  
  public void merge(Object entity)
  {
    getHibernateTemplate().merge(entity);
  }
  
  public void modify(Object entity)
  {
    getHibernateTemplate().update(entity);
  }
  
  public Serializable save(Object entity)
  {
    return getHibernateTemplate().save(entity);
  }
  
  public void saveOrUpdate(Object entity)
  {
    getHibernateTemplate().saveOrUpdate(entity);
  }
  
  public void remove(Object entity)
  {
    getHibernateTemplate().delete(entity);
  }
  
  public List<T> findAll()
  {
    return getHibernateTemplate().find(
      "from " + this.entityClass.getSimpleName());
  }
  
  public void removeAll()
  {
    List<T> list = getHibernateTemplate().loadAll(this.entityClass);
    getHibernateTemplate().deleteAll(list);
  }
  
  public PagerModel findByPager(int offset, int pageSize)
  {
    Criteria criteria = getSession().createCriteria(getEntityClass());
    int total = countByCriteria(criteria);
    criteria.setProjection(null);
    
    criteria.setFirstResult(offset);
    criteria.setMaxResults(pageSize);
    List<T> list = criteria.list();
    PagerModel pm = new PagerModel(offset, pageSize);
    pm.setTotal(total);
    pm.setList(list);
    return pm;
  }
  
  public PagerModel findByPager(ConditionType conditionType, int offset, int pageSize)
  {
    if (conditionType == null) {
      return findByPager(offset, pageSize);
    }
    List<ConditionType> conditionTypes = new ArrayList();
    conditionTypes.add(conditionType);
    Criteria criteria = createCriteria(conditionTypes);
    
    int total = countByCriteria(criteria);
    
    criteria.setProjection(null);
    criteria.setFirstResult(offset);
    criteria.setMaxResults(pageSize);
    
    List<T> list = criteria.list();
    
    PagerModel pm = new PagerModel(offset, pageSize);
    pm.setTotal(total);
    pm.setList(list);
    return pm;
  }
  
  public PagerModel findByPager(List<ConditionType> conditionTypes, int offset, int pageSize)
  {
    if (conditionTypes == null) {
      return findByPager(offset, pageSize);
    }
    Criteria criteria = createCriteria(conditionTypes);
    
    int total = countByCriteria(criteria);
    
    criteria.setProjection(null);
    criteria.setFirstResult(offset);
    criteria.setMaxResults(pageSize);
    
    List<T> list = criteria.list();
    
    PagerModel pm = new PagerModel(offset, pageSize);
    pm.setTotal(total);
    pm.setList(list);
    return pm;
  }
  
  public PagerModel findByPager(List<ConditionType> conditionTypes, List<Order> orders, int offset, int pageSize)
  {
    if (orders == null) {
      return findByPager(conditionTypes, offset, pageSize);
    }
    Criteria criteria = createCriteria(conditionTypes);
    
    int total = countByCriteria(criteria);
    
    criteria.setProjection(null);
    for (int i = 0; i < orders.size(); i++) {
      criteria.addOrder((Order)orders.get(i));
    }
    criteria.setFirstResult(offset);
    criteria.setMaxResults(pageSize);
    
    List<T> list = criteria.list();
    
    PagerModel pm = new PagerModel(offset, pageSize);
    pm.setPagesize(pageSize);
    pm.setTotal(total);
    pm.setList(list);
    return pm;
  }
}
